package com.gitee.wsl.ext.collection

import com.gitee.wsl.base.Objects
import com.gitee.wsl.ext.map.removeEmpty


/**
 * Extension function for removing empty maps and collections from an Iterable.
 *
 * Returns a new list with empty maps and collections removed.
 *
 * @param maps If true, empty maps will be removed. Default is true.
 * @param collections If true, empty collections will be removed. Default is true.
 * @return A new list with empty maps and collections removed.
 */
fun <T> Iterable<T>.removeEmpty(maps: Boolean = true, collections: Boolean = true): List<T> {
    if (!maps && !collections) return this.toList()

    val result = mutableListOf<T>()
    for (element in this) {
        when {
            maps && element is Map<*, *> -> {
                val cleanedMap = element.removeEmpty(true, collections)
                if (cleanedMap.isNotEmpty()) {
                    @Suppress("UNCHECKED_CAST")
                    result.add(cleanedMap as T)
                }
            }
            collections && element is Iterable<*> -> {
                val cleanedCollection = element.removeEmpty(maps, true)
                if (cleanedCollection.isNotEmpty()) {
                    @Suppress("UNCHECKED_CAST")
                    result.add(cleanedCollection as T)
                }
            }
            else -> result.add(element)
        }
    }
    return result
}


inline fun <E> MutableIterable<E>.removeFirstWhere(test: (E) -> Boolean): Boolean {
    val iterator = iterator()
    while (iterator.hasNext()) {
        if (test(iterator.next())) {
            iterator.remove()
            return true
        }
    }

    return false
}

/**
 * Removes the specified number of elements from the start index in the collection and returns them.
 * This method modifies the input collections.
 *
 * @param <E>  the type of object the [Collection] contains
 * @param input  the collection will be operated, can't be null
 * @param startIndex  the start index (inclusive) to remove element, can't be less than 0
 * @param count  the specified number to remove, can't be less than 1
 * @return collection of elements that removed from the input collection
 * @throws NullPointerException if input is null
 * @since 4.5.0-M1
</E> */
fun <E> MutableCollection<E>.removeCount( startIndex: Int, count: Int): Collection<E> {
    var startIndex = startIndex
    var count = count

    if (startIndex < 0) {
        throw IndexOutOfBoundsException("The start index can't be less than 0.")
    }
    if (count < 0) {
        throw IndexOutOfBoundsException("The count can't be less than 0.")
    }
    if (size < startIndex + count) {
        throw IndexOutOfBoundsException("The sum of start index and count can't be greater than the size of collection.")
    }

    val result = ArrayList<E>(count)
    val iterator = iterator()
    while (count > 0) {
        if (startIndex > 0) {
            startIndex -= 1
            iterator.next()
            continue
        }
        count -= 1
        result.add(iterator.next())
        iterator.remove()
    }
    return result
}


/**
 * Removes elements whose index are between startIndex, inclusive and endIndex,
 * exclusive in the collection and returns them.
 * This method modifies the input collections.
 *
 * @param <E>  the type of object the [Collection] contains
 * @param input  the collection will be operated, must not be null
 * @param startIndex  the start index (inclusive) to remove element, must not be less than 0
 * @param endIndex  the end index (exclusive) to remove, must not be less than startIndex
 * @return collection of elements that removed from the input collection
 * @throws NullPointerException if input is null
 * @since 4.5.0-M1
</E> */
fun <E> MutableCollection<E>.removeRange(startIndex: Int, endIndex: Int): Collection<E> {

    require(endIndex >= startIndex) { "The end index can't be less than the start index." }
    if (size < endIndex) {
        throw IndexOutOfBoundsException("The end index can't be greater than the size of collection.")
    }
    return removeCount(startIndex, endIndex - startIndex)
}
